﻿using System;
using System.Collections.Generic;

using System.Linq;
using Nop.Core;
using Nop.Core.Data;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using MongoDB.Driver.Builders;


namespace Nop.Data.Mongodb
{
    /// <summary>
    /// Mongodb repository
    /// </summary>
    //http://www.cnblogs.com/acgk/articles/3448701.html MongodbRepository
    public partial class MongodbRepository<T> : IRepository<T> where T : BaseEntity
    {

        private const string ConnectionString = "mongodb://localhost";

        private MongoDatabase _db;
        public MongoDatabase Database { get; private set; }
        private MongoCollection<T> collection;

        public string _collectionName;
        public MongodbRepository()
        {
            //Connecting to MongoDB database in MVC applications http://www.tuicool.com/articles/6vAj2i 
            try
            {
                MongoClient client = new MongoClient(ConnectionString); // connect to localhost
                MongoServer server = client.GetServer();
                server.Connect();

                _db = server.GetDatabase("nop33");
                this.collection = _db.GetCollection<T>(typeof(T).Name);


                //  var s = _collection.FindAllAs;
                this._collectionName = typeof(T).Name;
            }
            catch (MongoConnectionException ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }

        //see:https://github.com/tekmaven/NServiceBus.Persistence.MongoDb/blob/master/src/NServiceBus.Persistence.MongoDb/Repository/MongoDbRepository.cs
        public T GetById(object id)
        {
            //var collection = _db.GetCollection(typeof(T).Name));
            //return collection.FindOneAs<T>(Query.EQ(fieldName, BsonValue.Create(value)));
          
            //var query = Query.EQ("Id", id.ToString());
            //var result = _db.GetCollection<T>(typeof(T).Name).FindOne(query);

            var result= this.collection.FindOneAs<T>(Query.EQ("Id", BsonValue.Create(id)));
            return result;
           



        }

        public virtual void Insert(T entity)
        {
            entity.Id =(int) getNextSequence();
            _db.GetCollection<T>(typeof(T).Name).Insert(entity);
        }

        public virtual void Update(T entity)
        {
            //this.collection.Save<T>(entity);
         //   var old = _db.GetCollection<T>(typeof(T).Name).Find(Query.EQ("Id", entity.Id));
            //var oldentity = this.GetById(entity.Id);
            //entity.MongodbId = oldentity.MongodbId;

            _db.GetCollection<T>(typeof(T).Name).Save(entity);
        }

        public void Delete(long id)
        {
            this.collection.Remove(Query.EQ("Id", id));
        }
        public virtual void Delete(T entity)
        {
            // this.collection.Remove(Query.EQ("_id", id));
            this.Delete(entity.Id);
            // _db.GetCollection<T>(typeof(T).Name).Remove(entity);
        }

        public IQueryable<T> Table
        {
            get
            {
                MongoCollection result = _db.GetCollection<T>(typeof(T).Name);
                var result1 = result.Count();

                return _db.GetCollection<T>(typeof(T).Name).AsQueryable<T>();
            }
        }

        #region Utilities
        /// <summary>
        /// int IIDGenerator
        /// </summary>
        private Int32 getNextSequence()
        {
            //"Auto-increment" is always a problem when working with a distributed system, as it creates a bottle-neck: Each new increment needs to read the previous data .. like some other parallel requests.

//Generally speaking, "auto-increment" restricts parallelism, specially on distributed systems.

            //http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/
            //            http://docs.mongodb.org/manual/reference/command/findAndModify/
            //            FindAndModify方法
            //使用FindAndModify方法,你可以在一个原子操作里面查找一个匹配的文档并且修改更新.FindAndModify通常用于单个的文档，如果匹配了多个文档，可以使用标准的排序方法匹配到你自己想要修改的文档。
            //可以看下面文档使用FindAndModify
            //http://www.mongodb.org/display/DOCS/findAndModify+Command
            //调用FindAndModify的方法如下：
            //var jobs = database.GetCollection("jobs");
            //var query = Query.And(
            //    Query.EQ("inprogress", false),
            //    Query.EQ("name", "Biz report")
            //);
            //var sortBy = SortBy.Descending("priority");
            //var update = Update.
            //    .Set("inprogress", true)
            //    .Set("started", DateTime.UtcNow);
            //var result = jobs.FindAndModify(
            //    query,
            //    sortBy,
            //    update,
            //    true // return new document
            //);
            //var chosenJob = result.ModifiedDocument;

            
           
            

            string name = this._collectionName;

         
            FindAndModifyArgs args = new FindAndModifyArgs();
            args.Query = Query.EQ("_id", name);
            args.Update = MongoDB.Driver.Builders.Update.Inc("seq", 1);
            args.Upsert = true;
            args.VersionReturned = FindAndModifyDocumentVersion.Modified;
            // var ret = _db.GetCollection("Ids").FindAndModify(query,null,update,true);
            var result = _db.GetCollection("Ids").FindAndModify(args);

           
            BsonValue value = (BsonValue)result.ModifiedDocument["seq"];
            return value.AsInt32;




        }
        #endregion


    }
}
